﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using _5E.Utils.CollectionHelper.GenericExtension;
using _5E.Utils.DbHelper.EasySQL;
using _5E.Utils.DbHelper.EasySQL.Creator;
using _5E.Utils.DbHelper.ParameterParser;

namespace _5E.Utils.DbHelper
{
    /// <summary>
    /// 程序说明：简易DB
    /// 创建作者：ahl5esoft
    /// 创建时间：2012-04-17
    /// </summary>
    /// <paratypeparam name="T">实体类</paratypeparam>
    public class EasyDb<T> : ITransactable where T : new()
    {
        #region 变量

        private const string TRUNCATE_FORMAT = "TRUNCATE TABLE {0}";

        private static EasyDbTableCache s_EasyDbTableCache = new EasyDbTableCache();

        private string[] m_Columns = null;

        private string m_Where = null;

        private Dictionary<string, List<DbParameter[]>> m_TransCache = new Dictionary<string, List<DbParameter[]>>();

        private bool m_IsTransaction = false;

        #endregion

        #region 实例属性

        public string CacheKey
        {
            get
            {
                var table = s_EasyDbTableCache.GetEasyDbTable(typeof(T));
                var pkColumns = table.Columns.Where(c => c.ColumnType == EasyDbColumnType.PK);
                return null != pkColumns && 1 == pkColumns.Count() ? pkColumns.First().PropertyName : null;
            }
        }

        #endregion

        #region 实例方法

        protected EasyDb<T> SetColumns(params string[] columns)
        {
            m_Columns = columns;
            return this;
        }

        protected EasyDb<T> SetWhere(string where)
        {
            m_Where = where;
            return this;
        }

        private string CreateSQL<TCreator>() where TCreator : BaseCreator, new()
        {
            var creator = new TCreator
            {
                Table = s_EasyDbTableCache.GetEasyDbTable(typeof(T))
            };
            if (m_Columns != null && 0 < m_Columns.Length && creator is ICustomColumn)
            {
                (creator as ICustomColumn).SetColumns(m_Columns);
            }
            if (!string.IsNullOrEmpty(m_Where) && creator is ICustomWhere)
            {
                (creator as ICustomWhere).SetWhere(m_Where);
            }
            var sql = creator.CreateSQL();
            m_Columns = null;
            m_Where = null;
            return sql;
        }

        public void Begin()
        {
            m_IsTransaction = true;
            m_TransCache = new Dictionary<string, List<DbParameter[]>>();
        }

        public void Prepare(string sql, DbParameter[] dbParameters)
        {
            m_TransCache.TryAdd(sql, dbParameters);
        }

        public bool Commit()
        {
            m_IsTransaction = false;
            var success = false;
            if (null != m_TransCache && 0 < m_TransCache.Count)
            {
                try
                {
                    var db = DbFactory.BuildDb();
                    db.ExecuteBatchNonQuery(m_TransCache);
                    success = true;
                }
                catch (DbException)
                {
                    throw;
                }
            }
            return success;
        }

        #endregion

        #region Query

        public T GetData(T entity)
        {
            var whereSQL = s_EasyDbTableCache.GetEasyDbTable(typeof(T)).PKWhereSQL;
            SetWhere(whereSQL);
            var list = GetList(entity);
            return list.FirstOrDefault();
        }

        public List<T> GetList(T entity)
        {
            var parser = DbFactory.BuildEntityParser(entity);
            var list = GetList(parser);
            return list;
        }

        public T GetData(Hashtable parameters)
        {
            var list = GetList(parameters);
            return list.FirstOrDefault();
        }

        public List<T> GetList(Hashtable parameters)
        {
            var parser = DbFactory.BuildHashtableParser(parameters);
            var list = GetList(parser);
            return list;
        }

        public T GetData(IParameterParser<DbParameter> parser)
        {
            var list = GetList(parser);
            return list.FirstOrDefault();
        }

        public List<T> GetAll()
        {
            IParameterParser<DbParameter> parser = null;
            var list = GetList(parser);
            return list;
        }

        public List<T> GetList(IParameterParser<DbParameter> parser)
        {
            var sql = CreateSQL<QueryCreator>();
            var dbParameters = new DbParameter[] { };
            if (null != parser)
            {
                dbParameters = parser.Parse(ref sql);
            }
            var db = DbFactory.BuildDb();
            var dataReader = db.GetDataReader(sql, dbParameters);
            var dataReaderHelper = new DataReaderHelper(dataReader);
            var list = dataReaderHelper.ToList<T>();
            return list;
        }

        #endregion

        #region NonQuery

        public void Save(T entity, ITransactable transDAL = null)
        {
            if (null == m_Columns || 0 == m_Columns.Length)
            {
                var table = s_EasyDbTableCache.GetEasyDbTable(typeof(T));
                var propertyNames = Array.ConvertAll(table.Columns, c => c.PropertyName);
                SetColumns(propertyNames);
            }
            var sql = CreateSQL<InsertCreator>();
            ExecuteNonQuery(sql, entity, transDAL);
        }

        public void Update(T entity, ITransactable transDAL = null)
        {
            var table = s_EasyDbTableCache.GetEasyDbTable(typeof(T));
            if (null == m_Columns || 0 == m_Columns.Length)
            {
                var dbColumns = Array.FindAll(table.Columns, c => c.ColumnType != EasyDbColumnType.PK);
                var propertyNames = Array.ConvertAll(dbColumns, c => c.PropertyName);
                SetColumns(propertyNames);
            }
            if (null == m_Where)
            {
                SetWhere(table.PKWhereSQL);
            }
            var sql = CreateSQL<UpdateCreator>();
            ExecuteNonQuery(sql, entity, transDAL);
        }

        public void Delete(T entity, ITransactable transDAL = null)
        {
            if (null == m_Where)
            {
                var table = s_EasyDbTableCache.GetEasyDbTable(typeof(T));
                SetWhere(table.PKWhereSQL);
            }
            var sql = CreateSQL<DeleteCreator>();
            ExecuteNonQuery(sql, entity, transDAL);
        }

        private void ExecuteNonQuery(string nonQuerySQL, T entity, ITransactable transDAL = null)
        {
            var sql = nonQuerySQL;
            var parser = DbFactory.BuildEntityParser(entity);
            var dbParameters = parser.Parse(ref sql);
            if (m_IsTransaction)
            {
                if (null == transDAL)
                {
                    Prepare(sql, dbParameters);
                }
                else
                {
                    transDAL.Prepare(sql, dbParameters);
                }
            }
            else
            {
                var db = DbFactory.BuildDb();
                db.ExecuteNonQuery(sql, dbParameters: dbParameters);
            }
        }

        #endregion
    }
}
